home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK2.toast / Development Kits (Disc 2) / OpenDoc Development Framework / Documentation / Engineering Notes / ODF Internet < prev    next >
Encoding:
Text File  |  1996-09-17  |  24.2 KB  |  348 lines  |  [TEXT/ttxt]

  1. OpenDoc
  2. Development
  3. Framework
  4.                                                                                                                                                                                      
  5. ODF Internet Engineering Note
  6. ODF Release 2                                                                                                                                                            
  7.  
  8. This document contains information needed to create a Cyberdog-savvy part.
  9.  
  10.  
  11. Table of Contents
  12. -------------------------
  13. • Feature Overview
  14. • Code Roadmap
  15. • Installation
  16. •    Generating a Cyberdog Part from ODFCyberStarter
  17. •    Customizing an ODFCyberStarter Part
  18. • Adding Cyberdog Support Using ODF
  19.     • Implementing a Display Part’s OpenCyberItem method (p. 80)
  20.     • Embedding a Display Part in a Navigator (p. 82)
  21.     • Initializing and Releasing Cyberdog Objects (p. 94)
  22.     • Reading and Writing Cyberdog Items (p. 98)
  23.     • Displaying Cyberdog Menus (p. 105)
  24.     • Implementing your Cyberdog Part Extension (p. 115)
  25. • FW_CCyberdogCallbacks
  26. • FW_CCyberdogHelper
  27. • FW_PCyberSink (FW_OCyberSink)
  28. • FW_CCyberStream
  29. • FW_CCyberBuffer
  30. • Issues
  31. • Known Problems
  32.  
  33.  
  34.  
  35. Feature Overview
  36.  
  37. The ODF Internet layer contains support for Cyberdog, Apple’s OpenDoc Internet architecture. Cyberdog defines a number of extensions that internet-savvy parts need to implement. ODF supports the primary features that are used to implement display parts. This document describes how to add ODF’s Cyberdog support to your part. If you are writing a server (or other) part, you can still use ODF but there is no specific support for the related extensions.
  38.  
  39. ODF enables you to add a great deal of Cyberdog support to your part. This includes the basics (opening from CyberItems and reading from CyberStreams), more complex recipes (embedding in the navigator), and advanced features like using threads with CyberStreams and having “live” data that updates itself even after it is placed in a document.
  40.  
  41. ODF includes two Cyberdog-savvy example parts: ODFBitmap and ODFCyberStarter.
  42.  
  43. For further information, you can join the ODF mailing list at <odf-interest@cilabs.org> to discuss ODF’s support for Cyberdog. There are also Cyberdog-specific newsgroups available from the Cyberdog web site at <http://cyberdog.apple.com/>.
  44.  
  45.  
  46.  
  47. Code Roadmap
  48.  
  49. The ODFInternet layer contains the following classes: 
  50.  
  51.  - FW_CCyberdogCallbacks (which wraps CyberPartExtension so you don’t have to subclass it)
  52.  - FW_CCyberdogHelper (a subclass of FW_CCyberdogCallbacks that has more support; you’ll normally use this one)
  53.  - FW_CCyberStream (a smart pointer for the CyberStream class)
  54.  - FW_CCyberBuffer (a smart pointer for the buffers you use to read from CyberStream) 
  55.  - FW_OCyberSink and FW_PCyberSink, a FW_CSink subclass and smart pointer which you can use to enable reading from CyberStream using the standard ODF stream classes
  56.  
  57. The ODFInternet layer is complemented by the FWThreads module (in the ODFOS layer), which contains a thread class, FW_CThread. The Cyberdog support code relies on threads in several places, so you will need to create instances of FW_CThread to execute your code.
  58.  
  59. ODFCyberStarter is a minimal part intended to be used as a template for creating new parts (with PartMaker). It contains no content or selection classes, just part and frame classes. It implements the necessary ODFInternet recipes so that it can be used in Cyberdog to download data in a thread, using the ODF stream/sink classes. The example has just enough content (a text handle and text-drawing code) so that you can use it as a replacement for the Cyberdog Text Viewer when experimenting. Naturally for your own part you will want to remove this code, which should require minimal effort due to the code size.
  60.  
  61. ODFBitmap demonstrates much more Cyberdog support. This includes Cyberdog service menus, "live" content (part can update itself with newer data from the network), appearing in the Cyberdog navigator, Save As (required for navigator parts), and of course using the ODF stream/sink classes and threads to download its data asynchronously. If you have an existing part and want to add Cyberdog support to it, ODFBitmap is definitely the place to look; it has comments for all the Cyberdog-related code explaining why it is there and whether you would want it.
  62.  
  63.  
  64.  
  65. Installation
  66.  
  67. To build Cyberdog parts, you must install the Cyberdog SDK into CodeWarrior. See the Getting Started: CodeWarrior Development:CodeWarrior Additions folder. (Note that the current Cyberdog code supports development with the CodeWarrior environment only. This will be rectified in future releases.)
  68.  
  69. • Copy the Cyberdog SDK folder into your CodeWarrior : MacOS Support : Headers folder (it consists of the Cyberdog 1.1 headers and shared library, and Internet Config headers).
  70.  
  71. • If you are adding Cyberdog support to an existing part (as opposed to an ODFCyberStarter-derivative), you will need to add some access paths to your project. They will depend on your build configuration (CWPPCDebug, etc).
  72. ODFDev:ODF:Internet:
  73. ODFDev:ODF:CWPPCDebug:
  74.  
  75. • Also you need to add these items to your project (again, only if this is a pre-existing part):
  76. Cyberdog.stub (in Cyberdog SDK)
  77. ODFInternet.lib (in ODF:CWPPCDebug)
  78.  
  79. • Finally you’ll want to add ODFRC and possibly SOMobjects™ TS to your CodeWarrior Plugins : Compilers folder.
  80.  
  81.  
  82.  
  83. Generating a Cyberdog Part from CyberStarter
  84.  
  85. You can use ODFCyberStarter as the basis for your parts. It is very simple (no content or selection classes), yet implements the basic Cyberdog recipes. Or you may read the next section (Recipes) to integrate Cyberdog support into a more complex part.
  86.  
  87. (1) Generate Your Part
  88. Use PartMaker Pro to create a part. Open the CyberStarter Template and type in your own part name and company. Generate the part (save it in your ODFDev folder so the relative paths are correct).
  89.  
  90. (2) Change the Bindings
  91. You need to change the part bindings, preferrably before you build it. These are the variables you will want to customize: kMimeType, kKind, kCategory, kImportMacOSType, and the four Finder icon OSTypes. Don’t forget to change kPartSignature in the Project preferences as well.
  92.  
  93. (3) Build Your Part
  94. Open your part's project (CWPPCDebug) in CodeWarrior and choose the Make menu item. The part will be compiled and linked. Your part is now in the CWPPCDebug folder.
  95.  
  96. (4) Install Your Part
  97.  Make an alias to your part and put the alias in your Editors folder. If you have ODFDev on a different disk than your system folder, then create an Editors folder at the top level of that disk and put the alias there.
  98.  
  99. (5) Configure Editor Setup
  100. If your part displays the same kind of data as other existing parts, then you need to make sure that yours has precedence by setting your part-editor preference in the Editor Setup Control Panel. Your part should not try to configure Editor Setup itself, nor should your installer. If a user has multiple parts for the same kind, they get to choose which part runs. If they only have one, then nothing needs to be configured. 
  101.  
  102. In the Editor Setup Control Panel, select your mime type and choose your editor. For example, under "text/plain" you may see both ODFCyberStarter and the Cyberdog Text Viewer. To ensure that your part (or ODFCyberStarter) is run, you would select your part. 
  103.  
  104. (6) Open Data
  105. Use the Cyberdog Connect To dialog and enter a URL which points to your kind of data. In the case of ODFCyberStarter, you would need a text (not html) URL, like <http://www.meer.net/~mlanett/csdemo.txt>. Note: ODFCyberStarter is a Cyberdog recipes sample, and not a great text viewer. When you’re done playing with it make sure to switch back to the Cyberdog Text Viewer so you have a decent text viewer (i.e. with scrolling and all that).
  106.  
  107. At this point, congratulations, you’ve built and run a Cyberdog part using ODF!
  108.  
  109.  
  110.  
  111. Customizing an ODFCyberStarter Part
  112.  
  113. ODFCyberStarter (and your derivative part) is a text viewer, but since it is just a sample, it’s not very good. It doesn’t show feedback as it loads the text, and it doesn’t scroll. What is illustrated is the minimal work you need to do to implement Cyberdog support in your part. Here are the important places in the source code:
  114.  
  115. Binding.h:
  116. Notice that in addition to the usual kEditor and kKind, we also have defined kMimeType, in this case "text/plain". If you want to download a different mime type you should change this. You will also need to change the kind and category to be appropriate. For example, if you want to display images (such as jpegs), you would choose a kMimeType of "image/jpeg", and category of kODCategoryPainting. Which kind you would choose is unclear, because they aren't necessarily registered (CILabs is in charge of this). In general you should always specify a standard kind; use a propietary one also if you need to, but be sure to always write out data to a kind which other parts can read. In the case of an image you might want to write out a PICT kind.
  117.  
  118. Part.h and Part.cpp:
  119. CPart creates an instance of FW_CCyberdogHelper and sets the OpenCyberItem callback to be LoadCyberItem; this way we avoid having to subclass it. You won't need to change LoadCyberItem very much. It starts downloading the data directly into the text handle (fDownloadedText); looping to read data as soon as it is available, and causing immediate updates on the frames.
  120.  
  121. Frame.h and Frame.cpp
  122. We've added a DrawUpdate routine. This is just like Draw, except that it doesn't erase the screen first. In fact Draw does an erase and then calls DrawUpdate. We do things like this so that our LoadCyberItem can call DrawUpdate numerous times, each time displaying more and more text. Note that this part is a very lame text viewer and uses TextBox, which erases the screen, causing blinking.
  123.  
  124. Part.r
  125. The binding is the least understood part of OpenDoc, the hardest to debug, and the most likely place to introduce errors when writing a part Cyberdog-savvy. The best reason to begin with ODFCyberStarter is because the binding is set up right. It depends only on the constants in Binding.h, except for the English-language user strings, so you shouldn't have to mess with it for a while.
  126.  
  127.  
  128.  
  129. Adding Cyberdog Support Using ODF
  130.  
  131. The Cyberdog Programmer’s Kit contains many recipes for adding Cyberdog support to an OpenDoc part. This section lists the book sections (and corresponding page numbers) where you should use the ODF support code instead. ODFBitmap implements many of the following recipes; you can search the code for "Cyberdog:" (notice the colon) and read the comments there.
  132.  
  133.  
  134. Implementing a Display Part’s OpenCyberItem method (p. 80)
  135.  
  136. You can do this by creating an instance of FW_CCyberdogHelper and calling the SetLoadCyberItemThreadProcedure method, passing your global function for opening the CyberItem. Alternatively, you can subclass FW_CCyberdogHelper and override the DoSetCyberItem method. You must also override or add code to your frame’s HandleWindowEvent to close the Cyberdog window. 
  137.  
  138. If you use the default FW_CCyberdogHelper class, you can use the global function FW_SupportCyberdogIfPresent to create an instance of this class at initialization time. If you subclass FW_CCyberdogHelper, you must check to see that the underlying platform supports Cyberdog and create your helper subclass if it does. See the section "FW_CCyberdogHelper" in this document for more information on creating an instance of this class.
  139.  
  140. ODFBitmap provides a global function for reading CyberItems. In ODFBitmap, see CBitmapPart::Initialize for information on how to create your FW_CCyberdogHelper class. See CBitmapPart::LoadCyberItem for information on reading CyberItems. See CBitmapFrame::HandleWindowEvent for information on closing the Cyberdog window.
  141.  
  142. Notice that with ODF, responding to the OpenCyberItem call, creating the stream, and downloading the data are all handled in one place, which makes your code much simpler.
  143.  
  144.  
  145.  
  146. Embedding a Display Part in a Navigator (p. 82)
  147.  
  148. All of this is handled for you by FW_CCyberdogHelper. If you are using the default FW_CCyberdogHelper class, pass FW_kUseNavigator to the FW_SupportCyberdogIfPresent function. Otherwise, you must specify the appropriate value when you initialize your FW_CCyberdogHelper subclass.
  149.  
  150.  
  151. Initializing and Releasing Cyberdog Objects (p. 94)
  152.  
  153. The FW_CCyberdogHelper class handles this for you.
  154.  
  155.  
  156. Reading and Writing Cyberdog Items (p. 98)
  157.  
  158. ODF contains utility methods so that you can read from and write to ODF streams. 
  159.  
  160. To read data, you can use the global function FW_ReadCyberItem (which reads a CyberItem from a sink and returns it) or FW_CCyberdogHelper::InternalizeCyberItem (which also reads data from a sink, but which sets the CyberItem to be the current one). 
  161.  
  162. To write data, you can use the global function FW_WriteCyberItem (which writes any CyberItem to a sink) or FW_CCyberdogHelper::ExternalizeCurrentCyberItem (which writes the current CyberItem to a sink for “persistent live” parts).
  163.  
  164.  See CyberStarter’s CPart::InternalizeContent and ExternalizeContent for examples.
  165.  
  166.  
  167. Displaying Cyberdog Menus (p. 105)
  168.  
  169. FW_CCyberdogHelper will manage the Cyberdog menus for you. If you are using the default FW_CCyberdogHelper class, pass FW_kUseCyberMenus to the FW_SupportCyberdogIfPresent function. Otherwise, specify the appropriate value when you initialize your FW_CCyberdogHelper subclass. 
  170.  
  171. You must override or add some code to your frame’s FocusStateChanged method. See ODFBitmap’s CBitmapPart::Initialize and CBitmapFrame::FocusStateChanged.
  172.  
  173.  
  174. Implementing your Cyberdog Part Extension (p. 115)
  175.  
  176. ODF comes with a subclass of CyberPartExtension, and a C++ wrapper class: FW_CCyberdogHelper. You only need to create an instance of the FW_CCyberdogHelper class. (See the section "FW_CCyberdogHelper" for information on how to do this.)
  177.  
  178. It’s up to you whether to use the Cyberdog menus ("Cyberdog", "Mail/News", etc) or navigator support. If you want the Cyberdog menus then you must reserve a range of command numbers for them as well. ODFBitmap defines the constant cCyberdogCommands in Defines.k. It’s best if you put the Cyberdog commands after yours since you don’t know how many items it will need.
  179.  
  180.  
  181.  
  182. FW_CCyberdogCallbacks
  183.  
  184. Cyberdog programming starts with creating your part's CyberPartExtension. ODF comes with a subclass called ODF_FW_OCyberPartExtension, and overrides all its methods. Each override calls an equivalent method in the C++ class FW_CCyberdogCallbacks, allowing you to work entirely in C++. You should never override FW_CCyberdogCallbacks directly but should instead override its subclass FW_CCyberdogHelper.
  185.  
  186. Most CyberPartExtension methods are called DoXXX (e.g. DoSetCyberItem). The exceptions are HandleOpenCyberItem and HandleCyberCommand. The reason for this is that if you override a DoXXX method, you don’t need to call the inherited base class method, but you must call the inherited method for the HandleXXX methods.
  187.  
  188. Note that FW_CCyberdogCallbacks is a callback class — you should not call its DoXXX and HandleXXX methods directly. If, for example, you wanted to programmatically load a certain CyberItem, and needed to keep your extension in sync, you would do something like:
  189.  
  190.   fHelper->GetExtension(ev)->SetCyberItem (ev, myCI, kODNULL);
  191.  
  192. and not:
  193.  
  194.         fHelper->DoSetCyberItem (ev, myCI, kODNULL);
  195.  
  196. This is because there is additional work being done in ODF_FW_OCyberPartExtension, which would get skipped if you called the C++ class directly. You can of course call the convenience accessor methods (GetPart, GetExtension, etc).
  197.  
  198.  
  199.  
  200. FW_CCyberdogHelper
  201.  
  202. FW_CCyberdogHelper is a subclass of FW_CCyberdogCallbacks that overrides some of its methods, adds some new utility methods, and implements some of the Cyberdog recipes. When implementing your part's Cyberdog callbacks, you should always subclass this class.
  203.  
  204. The following example shows you how to create the default FW_CCyberdogHelper class.
  205.  
  206.     void MyClass::Initialize (…)
  207.     {
  208.         FW_CPart::Initialize (ev, storageUnit, fromStorage);
  209.         fHelper = ::FW_SupportCyberdogIfPresent (ev, this, FW_kUseCyberMenus, cCyberdogCommands,
  210.                                                FW_kUseNavigator);
  211.     }
  212.  
  213. If you need to define a custom subclass of FW_CCyberdogHelper, you cannot use the FW_SupportCyberdogIfPresent method, but must instead manually create and initialize your custom subclass. Before creating your subclass you should always verify that Cyberdog is installed on the underlying platform. The following example shows you how to create a custom FW_CCyberdogHelper subclass:
  214.  
  215. if (FW_CyberdogIsInstalled(ev))
  216. {
  217.     FW_CCyberdogHelper* helper = new CMyCyberdogHelper (part);
  218.     try 
  219.     {
  220.         helper->Initialize (ev, FW_kUseCyberMenus, cMyBaseMenuCommand, FW_kUseNavigator);
  221.         part->AdoptEventHandler (ev, helper);
  222.     }
  223.     catch 
  224.     {
  225.         delete helper;
  226.         throw;
  227.     }
  228. }
  229.  
  230.  
  231. DoSetCyberItem (OpenCyberItem)
  232.  
  233. The recipes section describes implementing your OpenCyberItem method. Using FW_CCyberdogHelper, you will instead override DoSetCyberItem. Your implementation of the method can be otherwise the same as the Cyberdog documentation suggests, but ODF offers another approach.
  234.  
  235. ODF supports reading from CyberItems using streams and threads. Instead of overriding OpenCyberItem, you may instead wish to call SetLoadCyberItemThreadProcedure, passing in a pointer to a global function (and optional parameter). The default implemation of DoSetCyberItem will create a new thread and call this function.
  236.  
  237. Here is how you might do it:
  238.  
  239. In CMyPart::Initialize, set up an OpenCyberItem callback:
  240.  
  241.        fHelper = ::FW_SupportCyberdogIfPresent (…);
  242.        if (fHelper)
  243.            fHelper->SetLoadCyberItemThreadProcedure (LoadCyberItem, this);
  244.  
  245. and in CMyPart::LoadCyberItem (a static method), read the data:
  246.  
  247.     CMyPart* self = (CMyPart*) selfCPart;
  248.     CyberItem* item = self->fHelper->GetCyberItem(ev);
  249.     CyberStream* cStream = item->CreateCyberStream (ev);
  250.     cStream->Open (ev);
  251.     FW_PCyberSink sink (ev, cStream);
  252.     …
  253.  
  254. The "this" parameter passed to SetLoadCyberItemThreadProcedure has been passed back to the callback; the callback casts it to the part, gets the current CyberItem, and opens it and reads from it using an ODF sink class.
  255.  
  256. What will now happen is that LoadCyberItem will be run in its own thread, and the ODF sink class (FW_PCyberSink) will suspend the thread whenever it is waiting for data to arrive. This means that your code is simpler than if you were writing an idling/polling routine (as the Cyberdog sample code does).
  257.  
  258.  
  259.  
  260. FW_PCyberSink (FW_OCyberSink)
  261.  
  262. FW_PCyberSink allows you to read data from CyberStreams using ODF stream objects. This sink uses threads and sleeps when data is unavailable (e.g. slow network connection). The sleeping is transparent to you, so you never have to worry about buffer availablity. This is extremely useful when reading structured data, because there are frequently nested states, requiring you to write a suspendable state machine to read it. This is unnecessary with threads. FW_PCyberSink has blocking, synchronous semantics, and makes reading from the network as simple as reading from a disk file. 
  263.  
  264. ODFBitmap uses this sink to great advantage, because its JPEG glue code is written to read from an FW_OSink. ODF has sink classes for files, memory, and Cyberdog, so ODFBitmap can read JPEGs from any of these sources. Furthermore, the code doesn’t have to make any special allowances for data coming from the network. When the JPEG code asks for a 56-byte data structure, for instance, FW_OCyberSink will simply sleep until it all arrives. Without threads and FW_OCyberSink, if the network connection was slower than the computer (almost always), it is likely that less than 56 bytes would be available, requiring the code to either block or return a “non enough data” tag, set up a state to indicate where it was, and exit out and idle until more data arrived.  FW_OCyberSink banishes all that. To read N bytes, just call Read(dest,N), and you’ll (eventually) get them. If you want to avoid sleeping (i.e. you are displaying data incrementally), call Read(dest,GetAvailableBytes()).
  265.  
  266. As with all ODF sink classes, you can instantiate a stream on them. For instance:
  267.  
  268.     FW_PCyberSink sink (ev, myCI);
  269.     FW_CReadableStream s (ev, sink);
  270.     s >> myShort >> myLong >> myOtherThing;
  271.  
  272. The one limitation is that FW_PCyberSink can only be used from a secondary thread. This limitation is because the thread sleeps if no data is available, and it would be very bad to put the part's user interface thread to sleep. With secondary threads, you start a download (which might take several minutes) in a thread, but the user interface remains live and the user can continue to do things. In fact you will probably want to provide incremental display of your data (like most well-written web browsers do).
  273.  
  274. If you are using the default FW_CCyberdogHelper class, ODF creates a thread before calling your global function to read the CyberItem. If you subclass FW_CCyberdogHelper, you must create this thread manually in your overriding DoSetCyberItem method. 
  275.  
  276. If you are worried about data conflicts between threads, it’s not that huge a problem. For one thing, MacOS threads are cooperative, so you won’t have thread context switches at unexpected times. Also, using multiple threads instead of idling/polling is just a change in implementation, but both are concurrent. If you were doing idling/polling then you would already have the potential for data conflicts. Threads make things no worse and, in fact, can greatly simplify your code.
  277.  
  278. Note: FW_PCyberSink is actually a smart pointer class; it creates an instance of FW_OCyberSink, which does the real work. You generally don’t need to deal with it however.
  279.  
  280.  
  281.  
  282. FW_CCyberStream
  283.  
  284. FW_CCyberStream is an "envelope" class for CyberStreams. It ensures that the CyberStream is deleted when you are done using it. It even calls Abort if you weren't done reading from it. Here is an example of how to use one:
  285.  
  286.     FW_CCyberStream cs (myCyberItem->CreateCyberStream(ev));
  287.     short status = cs->GetStreamStatus(ev);
  288.     while (!(status & FW_kCyberStreamDone)) 
  289.     {
  290.         if (status & kCDDataAvailable) 
  291.         {
  292.             FW_CCyberBuffer ab (ev, cs);
  293.             do_something (ev, ab.GetBuffer(), ab.GetSize());
  294.         }
  295.         else
  296.             ::YieldToAnyThread();
  297.         status = cs->GetStreamStatus(ev);
  298.     }
  299.  
  300. With FW_CCyberStream you don't need to worry about exceptions, stream errors, or deleting the stream. These things are handled for you.
  301.  
  302.  
  303.  
  304. FW_CCyberBuffer
  305.  
  306. FW_CCyberBuffer is also an envelope class, this time for a buffer aquired from a CyberStream. You don’t need to use it if you use FW_PCyberSink, but it is available if you may prefer to work a bit closer to the data source. Here is what ordinary code would look like (not using this helper class): 
  307.  
  308.     Ptr buffer;
  309.     Size size;
  310.     cs->GetBuffer (ev, &buffer, &size);
  311.     FW_TRY 
  312.     {
  313.         MyUseTheBufferProc (ev, buffer, size);
  314.     }
  315.     FW_CATCH_BEGIN
  316.     FW_CATCH_EVERYTHING() 
  317.     {
  318.         cs->ReleaseBuffer (ev, buffer);
  319.         FW_THROW_SAME();
  320.     }
  321.     FW_CATCH_END
  322.     cs->ReleaseBuffer (ev, buffer);
  323.  
  324. You can simplify all that code to this: 
  325.  
  326.     FW_CCyberBuffer ab (ev, cs);
  327.     MyUseTheBufferProc (ev, ab.GetBuffer(), ab.GetSize());
  328.  
  329. With FW_CCyberBuffer you don't need to worry about exceptions or releasing the buffer.
  330.  
  331. Note: With a CyberStream buffer you have no control over the size: you can’t request exactly N bytes, and if you get too much, you can’t just hand back the excess. FW_PCyberSink handles all this for you, so it’s clearly the way to go.
  332.  
  333.  
  334.  
  335. Issues
  336.  
  337. The FW_OCyberStream class only works if it is run in a secondary thread. This is because it will block and suspend its thread, and this would be bad for the main (user interface) thread. However, ODF’s emulated exceptions package can not be used in a threaded environment; you must use a compiler with native exceptions. At the moment, CodeWarrior is the only such supported compiler. Thus, if you want to use FW_OCyberStream, you have to build your part with CodeWarrior and turn on native C++ exceptions (for your part and the framework).
  338.  
  339.  
  340.  
  341. Known Problems
  342.  
  343. - The service menu code doesn’t check to see if the menus were changed at runtime.
  344. - The OpenCyberItem recipe is actually implemented in SetCyberItem (which is how Cyberdog used to recommend doing it). This is harmless but it will be updated to the new recipe for R3.
  345.  
  346.  
  347. © 1993 - 1996 Apple Computer, Inc. All rights reserved.
  348. Apple, the Apple Logo, Macintosh, and OpenDoc are trademarks of Apple Computer, Inc., registered in the United States and other countries.